import React from 'react';
import {Select} from 'antd';
import {isArray, isEmpty, find, isFunction, keys, map, omit, isNumber} from 'lodash-es';
import BaseField from "../Base/BaseField";
import PropTypes from "prop-types";
import {comboApi} from "../Config/GlobalApi";
import {getDerivedStateFromPropsFunc} from "../Base/helper/lifeCycle";
import {getRandomKey, isObjectValEqual} from "jh-utils";
import ComboCache from "./ComboCache";

const {Option} = Select;

const staticProps = {

    /**
     *
     */
    selector: PropTypes.string,
    /**
     * 下拉列表数据
     *
     * */
    compData: PropTypes.arrayOf(PropTypes.object),
    /**
     * compData 对象数据的 主键 属性名
     * @default 'code'
     *
     * */
    primaryKey: PropTypes.string,
    /**
     * compData 对象数据的 名称 属性名
     * @default 'name'
     *
     * */
    nameKey: PropTypes.string,
    /**
     * 是否显示，如果为 false，则此组件隐藏不显示，但组件逻辑正常；
     * 仍然会请求数据，并在加载数据之后回调 onDataLoad
     * @default true
     *
     * */
    visible: PropTypes.bool,
    /**
     * 数据加载之后的回调
     * @param rsp object 从接口返回的数据
     *
     * */
    onDataLoad: PropTypes.func,
    /**
     * needOptionLocale <option>节点的值是否需要进行国际化处理
     *
     * */
    needOptionLocale: PropTypes.bool,
    /**
     * 选择后的回调
     * @param changeValue string 改变后的值
     *
     * */
    onChange: PropTypes.func,
    /**
     * 样式；会直接传给 antd 的 Select 组件的 style
     *
     * */
    style: PropTypes.object,
    /**
     * Function 请求方法
     *
     * */
    fetchList: PropTypes.func,
    value: PropTypes.any,
    /**
     * 是否需要显示 placeholder。
     * @default true
     */
    needPH: PropTypes.bool,
    readonly: PropTypes.bool
};

/**
 * 枚举下拉框
 */
export default class ComboBase extends BaseField {

    static propTypes = {
        ...BaseField.propTypes,
        ...staticProps
    };

    static defaultProps = {
        primaryKey: 'code',
        nameKey: 'name',
        visible: true,
        needPH: true
    };

    static getDerivedStateFromProps(nextProps, preState) {
        let reObj = getDerivedStateFromPropsFunc(nextProps, preState, (changedValue) => {
            // @param changedValue 即 nextProps 中有变化的 value 或 defaultValue 的值    。
            // 在这个方法里返回需要更新到 state 里的值。
            // （注：defaultValue 的处理已经在 getDerivedStateFromPropsFunc 中默认处理，所以这里不用返回了）
            return {
                value: isNumber(changedValue) ? changedValue : changedValue || '',
                originValue: changedValue
            };
        });

        if (!isObjectValEqual(nextProps, {compData: preState.propList}, ['compData'])) {
            return {
                ...(reObj || {}),
                list: nextProps.compData || [],
                propList: nextProps.compData,
            }
        }
        return reObj;
    }

    /**
     *
     * @type {{}}
     */
    set listCache(value) {
        ComboCache.addCache(this.getPropsByName('selector'), value);
    }

    get listCache() {
        return ComboCache.getCache(this.getPropsByName('selector'));
    }

    set loadingList(value) {
        ComboCache.loading[this.getPropsByName('selector')] = value;
    }

    get loadingList() {
        return ComboCache.loading[this.getPropsByName('selector')];
    }

    abortCtrl = {};

    randomKey = getRandomKey();

    moreCommonProps() {
        return [
            ...super.moreCommonProps(),
            ...keys(staticProps)
        ]
    }

    initState(props) {
        return {
            ...super.initState(props),
            value: props.value || props.defaultValue || '',
            loading: false,
            list: props.compData || [],
            propList: props.compData
        };
    }

    componentDidMount() {
        super.componentDidMount && super.componentDidMount();

        this.fetchData();
    }

    componentWillUnmount() {
        super.componentWillUnmount();

        const {selector} = this.getProps();
        if (selector) {
            ComboCache.removeLoadingIns(selector, this.randomKey)
        }
    }


    fetchData() {
        this.runBeforeCbs();

        // 先读缓存
        const listCache = this.listCache;
        // console.log(`【${this.props.selector}】 fetchData`, listCache);
        if (!isEmpty(listCache)) {

            this.setState({
                list: listCache
            }, () => {
                this.runPropsFunc('onDataLoad')(listCache);
            });

        } else {

            const $this = this;
            const {appins, selector, fetchList} = this.getProps();

            if (isFunction(fetchList)) {
                $this.setState({loading: true});
                this._afterFetchList(fetchList({code: selector}));

            } else {
                const fetchFunc = appins && appins.fetchCombo ? appins.fetchCombo.bind(appins) : ( // 取 appins.fetchCombo
                    this.fetchFunc.bind(this) // 取 this.fetchFunc
                );

                if (selector && isFunction(fetchFunc)) {

                    if (this.loadingList) {
                        ComboCache.addLoadingIns(selector, this.randomKey, this);
                        this.setState({loading: true});
                        return false;
                    }

                    this.loadingList = true;
                    $this.setState({loading: true});

                    this._afterFetchList(fetchFunc({
                        code: selector
                    }));
                }

            }
        }

    }

    _afterFetchList(fetchReturn) {
        const {selector, onDataLoad} = this.getProps();

        if (fetchReturn instanceof Promise) {
            fetchReturn.then((rsp) => {
                let newList = [];

                if (rsp && rsp.status === 200 && rsp.data) {
                    const rspData = rsp.data;
                    newList = isArray(rspData) ? rspData : (
                        isArray(rspData.options) ? rspData.options : []
                    )
                }

                this.setState({
                    list: newList,
                    loading: false
                }, () => {
                    isFunction(onDataLoad) && onDataLoad(newList);
                });

                if (selector) {
                    this.listCache = newList;
                    this.loadingList = false;
                    ComboCache.updateComboInstances(selector);
                }
            });
        } else {
            this.setState({loading: false});
            if (selector) {
                this.loadingList = false;
                ComboCache.updateComboInstances(selector);
            }
        }
    }

    fetchFunc(ajaxData, options = {}) {
        // console.log('fetchFunc')
        return this.reqGet(comboApi.api, ajaxData, options);
    }

    /**
     * “点击下拉框”的回调
     * 如果下拉列表为空，则刷新数据
     * @param open {boolean} 当前是否打开了下拉框
     */
    hdlClickSelect(open) {
        // console.log('hdlClickSelect open', open);
        if (open) {
            if (isEmpty(this.state.list)) {
                this.fetchData();
            } else {
                this.runBeforeCbs();
            }
        }
    }

    mainRender(context) {
        const props = this.getProps();
        const {visible} = props;
        if (false === visible) return null;

        const {needPH, style, primaryKey, nameKey, compData, needOptionLocale, readonly, disabled} = props;
        const {localeUtils} = this.helpers;
        let optData = [];
        if (!isEmpty(compData)) {
            optData = compData;
        } else if (!isEmpty(this.state.list)) {
            optData = this.state.list;
        }
        const styleO = style || {};
        let placeHolder = !isEmpty(localeUtils) ? localeUtils.myFormatMessage('comp.select.placeholder') : '请选择';
        let optionMoreProps = {
            text: placeHolder
        };
        let val = 'code' === primaryKey ? `${this.state.value}` : this.state.value;
        const cProps = omit(props, this.commonProps);

        // 只读形式
        if (readonly || disabled) {
            let optItem = find(optData, (opt) => {
                return opt && opt[primaryKey] === val;
            });
            return (
                <span className={'form-item-text'}>{optItem ? optItem.name : ''}</span>
            )
        }
        // 如果启用搜索，则添加默认过滤逻辑
        if (cProps.showSearch && undefined === cProps.filterOption) {

            cProps.filterOption = (input, opt) => {
                // children: "name2"
                // code: "2"
                // key: "1"
                // spellCode: ""
                // title: "name2"
                // value: "2"

                let inputVal = `${input || ''}`.toLocaleLowerCase();
                const text = `${opt.children || ''}`.toLocaleLowerCase();
                let isOk = -1 !== text.indexOf(inputVal);

                if (!isOk && opt.title) {
                    const title = `${opt.title || ''}`.toLocaleLowerCase();
                    isOk = -1 !== title.indexOf(inputVal);
                }
                if (!isOk && opt.code) {
                    const code = `${opt.code || ''}`.toLocaleLowerCase();
                    isOk = -1 !== code.indexOf(inputVal);
                }
                if (!isOk && opt.spellcode) {
                    const spellCode = `${opt.spellcode || ''}`.toLocaleLowerCase();
                    isOk = -1 !== spellCode.indexOf(inputVal);
                }
                return isOk;
            }
        }

        return (
            <Select
                {...cProps}
                value={val}
                onDropdownVisibleChange={this.hdlClickSelect.bind(this)}
                onChange={this.hdlValueChange.bind(this)}
                loading={this.state.loading}
                virtual={false}
                style={{minWidth: '100px', ...styleO}}
                ref={(ref) => {
                    this.nodeRef = ref;
                }}
            >
                {
                    needPH && (
                        <Option value="" {...optionMoreProps}>{placeHolder}</Option>
                    )
                }
                {map(optData, (opt, oK) => {
                    let showName = needOptionLocale ? this.formatMsgByCn(opt[nameKey]) : opt[nameKey];
                    return <Option key={oK}
                                   value={opt[primaryKey]}
                                   title={showName}
                                   code={opt.code || ''}
                                   spellcode={opt.spellCode || ''}
                    >
                        {showName}
                    </Option>
                })}
            </Select>
        );
    }
}